(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
    else if (typeof define === 'function' && define.amd)
        define("StateMachine", [], factory);
    else if (typeof exports === 'object')
        exports["StateMachine"] = factory();
    else
        root["StateMachine"] = factory();
})(this, function () {
    return /******/ (function (modules) { // webpackBootstrap
        /******/ // The module cache
        /******/
        var installedModules = {};
        /******/
        /******/ // The require function
        /******/
        function __webpack_require__(moduleId) {
            /******/
            /******/ // Check if module is in cache
            /******/
            if (installedModules[moduleId]) {
                /******/
                return installedModules[moduleId].exports;
                /******/
            }
            /******/ // Create a new module (and put it into the cache)
            /******/
            var module = installedModules[moduleId] = {
                /******/
                i: moduleId,
                /******/
                l: false,
                /******/
                exports: {}
                /******/
            };
            /******/
            /******/ // Execute the module function
            /******/
            modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
            /******/
            /******/ // Flag the module as loaded
            /******/
            module.l = true;
            /******/
            /******/ // Return the exports of the module
            /******/
            return module.exports;
            /******/
        }
        /******/
        /******/
        /******/ // expose the modules object (__webpack_modules__)
        /******/
        __webpack_require__.m = modules;
        /******/
        /******/ // expose the module cache
        /******/
        __webpack_require__.c = installedModules;
        /******/
        /******/ // identity function for calling harmony imports with the correct context
        /******/
        __webpack_require__.i = function (value) {
            return value;
        };
        /******/
        /******/ // define getter function for harmony exports
        /******/
        __webpack_require__.d = function (exports, name, getter) {
            /******/
            if (!__webpack_require__.o(exports, name)) {
                /******/
                Object.defineProperty(exports, name, {
                    /******/
                    configurable: false,
                    /******/
                    enumerable: true,
                    /******/
                    get: getter
                    /******/
                });
                /******/
            }
            /******/
        };
        /******/
        /******/ // getDefaultExport function for compatibility with non-harmony modules
        /******/
        __webpack_require__.n = function (module) {
            /******/
            var getter = module && module.__esModule ?
                /******/
                function getDefault() {
                    return module['default'];
                } :
                /******/
                function getModuleExports() {
                    return module;
                };
            /******/
            __webpack_require__.d(getter, 'a', getter);
            /******/
            return getter;
            /******/
        };
        /******/
        /******/ // Object.prototype.hasOwnProperty.call
        /******/
        __webpack_require__.o = function (object, property) {
            return Object.prototype.hasOwnProperty.call(object, property);
        };
        /******/
        /******/ // __webpack_public_path__
        /******/
        __webpack_require__.p = "";
        /******/
        /******/ // Load entry module and return exports
        /******/
        return __webpack_require__(__webpack_require__.s = 5);
        /******/
    })
    /************************************************************************/
    /******/
    ([
        /* 0 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            module.exports = function (target, sources) {
                var n, source, key;
                for (n = 1; n < arguments.length; n++) {
                    source = arguments[n];
                    for (key in source) {
                        if (source.hasOwnProperty(key))
                            target[key] = source[key];
                    }
                }
                return target;
            }


            /***/
        }),
        /* 1 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            //-------------------------------------------------------------------------------------------------

            var mixin = __webpack_require__(0);

            //-------------------------------------------------------------------------------------------------

            module.exports = {

                build: function (target, config) {
                    var n, max, plugin, plugins = config.plugins;
                    for (n = 0, max = plugins.length; n < max; n++) {
                        plugin = plugins[n];
                        if (plugin.methods)
                            mixin(target, plugin.methods);
                        if (plugin.properties)
                            Object.defineProperties(target, plugin.properties);
                    }
                },

                hook: function (fsm, name, additional) {
                    var n, max, method, plugin,
                        plugins = fsm.config.plugins,
                        args = [fsm.context];

                    if (additional)
                        args = args.concat(additional)

                    for (n = 0, max = plugins.length; n < max; n++) {
                        plugin = plugins[n]
                        method = plugins[n][name]
                        if (method)
                            method.apply(plugin, args);
                    }
                }

            }

            //-------------------------------------------------------------------------------------------------


            /***/
        }),
        /* 2 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            //-------------------------------------------------------------------------------------------------

            function camelize(label) {

                if (label.length === 0)
                    return label;

                var n, result, word, words = label.split(/[_-]/);

                // single word with first character already lowercase, return untouched
                if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))
                    return label;

                result = words[0].toLowerCase();
                for (n = 1; n < words.length; n++) {
                    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();
                }

                return result;
            }

            //-------------------------------------------------------------------------------------------------

            camelize.prepended = function (prepend, label) {
                label = camelize(label);
                return prepend + label[0].toUpperCase() + label.substring(1);
            }

            //-------------------------------------------------------------------------------------------------

            module.exports = camelize;


            /***/
        }),
        /* 3 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            //-------------------------------------------------------------------------------------------------

            var mixin = __webpack_require__(0),
                camelize = __webpack_require__(2);

            //-------------------------------------------------------------------------------------------------

            function Config(options, StateMachine) {

                options = options || {};

                this.options = options; // preserving original options can be useful (e.g visualize plugin)
                this.defaults = StateMachine.defaults;
                this.states = [];
                this.transitions = [];
                this.map = {};
                this.lifecycle = this.configureLifecycle();
                this.init = this.configureInitTransition(options.init);
                this.data = this.configureData(options.data);
                this.methods = this.configureMethods(options.methods);

                this.map[this.defaults.wildcard] = {};

                this.configureTransitions(options.transitions || []);

                this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);

            }

            //-------------------------------------------------------------------------------------------------

            mixin(Config.prototype, {

                addState: function (name) {
                    if (!this.map[name]) {
                        this.states.push(name);
                        this.addStateLifecycleNames(name);
                        this.map[name] = {};
                    }
                },

                addStateLifecycleNames: function (name) {
                    this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);
                    this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);
                    this.lifecycle.on[name] = camelize.prepended('on', name);
                },

                addTransition: function (name) {
                    if (this.transitions.indexOf(name) < 0) {
                        this.transitions.push(name);
                        this.addTransitionLifecycleNames(name);
                    }
                },

                addTransitionLifecycleNames: function (name) {
                    this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);
                    this.lifecycle.onAfter[name] = camelize.prepended('onAfter', name);
                    this.lifecycle.on[name] = camelize.prepended('on', name);
                },

                mapTransition: function (transition) {
                    var name = transition.name,
                        from = transition.from,
                        to = transition.to;
                    this.addState(from);
                    if (typeof to !== 'function')
                        this.addState(to);
                    this.addTransition(name);
                    this.map[from][name] = transition;
                    return transition;
                },

                configureLifecycle: function () {
                    return {
                        onBefore: {
                            transition: 'onBeforeTransition'
                        },
                        onAfter: {
                            transition: 'onAfterTransition'
                        },
                        onEnter: {
                            state: 'onEnterState'
                        },
                        onLeave: {
                            state: 'onLeaveState'
                        },
                        on: {
                            transition: 'onTransition'
                        }
                    };
                },

                configureInitTransition: function (init) {
                    if (typeof init === 'string') {
                        return this.mapTransition(mixin({}, this.defaults.init, {
                            to: init,
                            active: true
                        }));
                    } else if (typeof init === 'object') {
                        return this.mapTransition(mixin({}, this.defaults.init, init, {
                            active: true
                        }));
                    } else {
                        this.addState(this.defaults.init.from);
                        return this.defaults.init;
                    }
                },

                configureData: function (data) {
                    if (typeof data === 'function')
                        return data;
                    else if (typeof data === 'object')
                        return function () {
                            return data;
                        }
                    else
                        return function () {
                            return {};
                        }
                },

                configureMethods: function (methods) {
                    return methods || {};
                },

                configurePlugins: function (plugins, builtin) {
                    plugins = plugins || [];
                    var n, max, plugin;
                    for (n = 0, max = plugins.length; n < max; n++) {
                        plugin = plugins[n];
                        if (typeof plugin === 'function')
                            plugins[n] = plugin = plugin()
                        if (plugin.configure)
                            plugin.configure(this);
                    }
                    return plugins
                },

                configureTransitions: function (transitions) {
                    var i, n, transition, from, to, wildcard = this.defaults.wildcard;
                    for (n = 0; n < transitions.length; n++) {
                        transition = transitions[n];
                        from = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]
                        to = transition.to || wildcard;
                        for (i = 0; i < from.length; i++) {
                            this.mapTransition({
                                name: transition.name,
                                from: from[i],
                                to: to
                            });
                        }
                    }
                },

                transitionFor: function (state, transition) {
                    var wildcard = this.defaults.wildcard;
                    return this.map[state][transition] ||
                        this.map[wildcard][transition];
                },

                transitionsFor: function (state) {
                    var wildcard = this.defaults.wildcard;
                    return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));
                },

                allStates: function () {
                    return this.states;
                },

                allTransitions: function () {
                    return this.transitions;
                }

            });

            //-------------------------------------------------------------------------------------------------

            module.exports = Config;

            //-------------------------------------------------------------------------------------------------


            /***/
        }),
        /* 4 */
        /***/
        (function (module, exports, __webpack_require__) {


            var mixin = __webpack_require__(0),
                Exception = __webpack_require__(6),
                plugin = __webpack_require__(1),
                UNOBSERVED = [null, []];

            //-------------------------------------------------------------------------------------------------

            function JSM(context, config) {
                this.context = context;
                this.config = config;
                this.state = config.init.from;
                this.observers = [context];
            }

            //-------------------------------------------------------------------------------------------------

            mixin(JSM.prototype, {

                init: function (args) {
                    mixin(this.context, this.config.data.apply(this.context, args));
                    plugin.hook(this, 'init');
                    if (this.config.init.active)
                        return this.fire(this.config.init.name, []);
                },

                is: function (state) {
                    return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);
                },

                isPending: function () {
                    return this.pending;
                },

                can: function (transition) {
                    return !this.isPending() && !!this.seek(transition);
                },

                cannot: function (transition) {
                    return !this.can(transition);
                },

                allStates: function () {
                    return this.config.allStates();
                },

                allTransitions: function () {
                    return this.config.allTransitions();
                },

                transitions: function () {
                    return this.config.transitionsFor(this.state);
                },

                seek: function (transition, args) {
                    var wildcard = this.config.defaults.wildcard,
                        entry = this.config.transitionFor(this.state, transition),
                        to = entry && entry.to;
                    if (typeof to === 'function')
                        return to.apply(this.context, args);
                    else if (to === wildcard)
                        return this.state
                    else
                        return to
                },

                fire: function (transition, args) {
                    return this.transit(transition, this.state, this.seek(transition, args), args);
                },

                transit: function (transition, from, to, args) {

                    var lifecycle = this.config.lifecycle,
                        changed = this.config.options.observeUnchangedState || (from !== to);

                    if (!to)
                        return this.context.onInvalidTransition(transition, from, to);

                    if (this.isPending())
                        return this.context.onPendingTransition(transition, from, to);

                    this.config.addState(to); // might need to add this state if it's unknown (e.g. conditional transition or goto)

                    this.beginTransit();

                    args.unshift({ // this context will be passed to each lifecycle event observer
                        transition: transition,
                        from: from,
                        to: to,
                        fsm: this.context
                    });

                    return this.observeEvents([
                        this.observersForEvent(lifecycle.onBefore.transition),
                        this.observersForEvent(lifecycle.onBefore[transition]),
                        changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,
                        changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,
                        this.observersForEvent(lifecycle.on.transition),
                        changed ? ['doTransit', [this]] : UNOBSERVED,
                        changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,
                        changed ? this.observersForEvent(lifecycle.onEnter[to]) : UNOBSERVED,
                        changed ? this.observersForEvent(lifecycle.on[to]) : UNOBSERVED,
                        this.observersForEvent(lifecycle.onAfter.transition),
                        this.observersForEvent(lifecycle.onAfter[transition]),
                        this.observersForEvent(lifecycle.on[transition])
                    ], args);
                },

                beginTransit: function () {
                    this.pending = true;
                },
                endTransit: function (result) {
                    this.pending = false;
                    return result;
                },
                failTransit: function (result) {
                    this.pending = false;
                    throw result;
                },
                doTransit: function (lifecycle) {
                    this.state = lifecycle.to;
                },

                observe: function (args) {
                    if (args.length === 2) {
                        var observer = {};
                        observer[args[0]] = args[1];
                        this.observers.push(observer);
                    } else {
                        this.observers.push(args[0]);
                    }
                },

                observersForEvent: function (event) { // TODO: this could be cached
                    var n = 0,
                        max = this.observers.length,
                        observer, result = [];
                    for (; n < max; n++) {
                        observer = this.observers[n];
                        if (observer[event])
                            result.push(observer);
                    }
                    return [event, result, true]
                },

                observeEvents: function (events, args, previousEvent, previousResult) {
                    if (events.length === 0) {
                        return this.endTransit(previousResult === undefined ? true : previousResult);
                    }

                    var event = events[0][0],
                        observers = events[0][1],
                        pluggable = events[0][2];

                    args[0].event = event;
                    if (event && pluggable && event !== previousEvent)
                        plugin.hook(this, 'lifecycle', args);

                    if (observers.length === 0) {
                        events.shift();
                        return this.observeEvents(events, args, event, previousResult);
                    } else {
                        var observer = observers.shift(),
                            result = observer[event].apply(observer, args);
                        if (result && typeof result.then === 'function') {
                            return result.then(this.observeEvents.bind(this, events, args, event))
                                .catch(this.failTransit.bind(this))
                        } else if (result === false) {
                            return this.endTransit(false);
                        } else {
                            return this.observeEvents(events, args, event, result);
                        }
                    }
                },

                onInvalidTransition: function (transition, from, to) {
                    throw new Exception("transition is invalid in current state", transition, from, to, this.state);
                },

                onPendingTransition: function (transition, from, to) {
                    throw new Exception("transition is invalid while previous transition is still in progress", transition, from, to, this.state);
                }

            });

            //-------------------------------------------------------------------------------------------------

            module.exports = JSM;

            //-------------------------------------------------------------------------------------------------


            /***/
        }),
        /* 5 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            //-----------------------------------------------------------------------------------------------

            var mixin = __webpack_require__(0),
                camelize = __webpack_require__(2),
                plugin = __webpack_require__(1),
                Config = __webpack_require__(3),
                JSM = __webpack_require__(4);

            //-----------------------------------------------------------------------------------------------

            var PublicMethods = {
                is: function (state) {
                    return this._fsm.is(state)
                },
                can: function (transition) {
                    return this._fsm.can(transition)
                },
                cannot: function (transition) {
                    return this._fsm.cannot(transition)
                },
                observe: function () {
                    return this._fsm.observe(arguments)
                },
                transitions: function () {
                    return this._fsm.transitions()
                },
                allTransitions: function () {
                    return this._fsm.allTransitions()
                },
                allStates: function () {
                    return this._fsm.allStates()
                },
                onInvalidTransition: function (t, from, to) {
                    return this._fsm.onInvalidTransition(t, from, to)
                },
                onPendingTransition: function (t, from, to) {
                    return this._fsm.onPendingTransition(t, from, to)
                },
            }

            var PublicProperties = {
                state: {
                    configurable: false,
                    enumerable: true,
                    get: function () {
                        return this._fsm.state;
                    },
                    set: function (state) {
                        throw Error('use transitions to change state')
                    }
                }
            }

            //-----------------------------------------------------------------------------------------------

            function StateMachine(options) {
                return apply(this || {}, options);
            }

            function factory() {
                var cstor, options;
                if (typeof arguments[0] === 'function') {
                    cstor = arguments[0];
                    options = arguments[1] || {};
                } else {
                    cstor = function () {
                        this._fsm.apply(this, arguments)
                    };
                    options = arguments[0] || {};
                }
                var config = new Config(options, StateMachine);
                build(cstor.prototype, config);
                cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance
                return cstor;
            }

            //-------------------------------------------------------------------------------------------------

            function apply(instance, options) {
                var config = new Config(options, StateMachine);
                build(instance, config);
                instance._fsm();
                return instance;
            }

            function build(target, config) {
                if ((typeof target !== 'object') || Array.isArray(target))
                    throw Error('StateMachine can only be applied to objects');
                plugin.build(target, config);
                Object.defineProperties(target, PublicProperties);
                mixin(target, PublicMethods);
                mixin(target, config.methods);
                config.allTransitions().forEach(function (transition) {
                    target[camelize(transition)] = function () {
                        return this._fsm.fire(transition, [].slice.call(arguments))
                    }
                });
                target._fsm = function () {
                    this._fsm = new JSM(this, config);
                    this._fsm.init(arguments);
                }
            }

            //-----------------------------------------------------------------------------------------------

            StateMachine.version = '3.0.1';
            StateMachine.factory = factory;
            StateMachine.apply = apply;
            StateMachine.defaults = {
                wildcard: '*',
                init: {
                    name: 'init',
                    from: 'none'
                }
            }

            //===============================================================================================

            module.exports = StateMachine;


            /***/
        }),
        /* 6 */
        /***/
        (function (module, exports, __webpack_require__) {

            "use strict";


            module.exports = function (message, transition, from, to, current) {
                this.message = message;
                this.transition = transition;
                this.from = from;
                this.to = to;
                this.current = current;
            }


            /***/
        })
        /******/
    ]);
});